/*!
 *	\file myfs_ialloc.c
 *
 *	в данном файле находятся функци для работы с картой и-узлов,
 *	удаления и создания нового и-узла
 *
 */

#include <linux/vfs.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/buffer_head.h>

#include "myfs.h"
#include "myfs_ialloc.h"


/*!
	 функция читает карту и-узлов в буфер
*/
static struct buffer_head *read_inode_bitmap(struct super_block *sb)
{
	struct myfs_sb_info *mbi = MYFS_SB(sb);
	if(!mbi->inode_bitmap) 
	{
		mbi->inode_bitmap = sb_bread(sb, MYFS_INODE_BM_BLOCK);
	}
	BUG_ON(!mbi->inode_bitmap);
	return mbi->inode_bitmap;
}

/*! 
	функция возвращает 1, если соответствующий и-узлу бит в карте битов 
	был обнулен, и возвращает 0 в обратном случае
*/
static int inode_already_freed(char *bitmap, unsigned int ino)
{
	int shift = (ino) / 8;		/* номер байта */
	int offset = (ino) % 8;		/* номер бита в байте */
	printk(KERN_ALERT "myfs: mask for inode(%u) = 0x%x.\n", ino, *(bitmap+shift));
	return !((1<<offset)&(*(bitmap+shift)));
}

/*!
	функция устанавливает соответствующий и-узлу бит в карте битов в 1
*/
void myfs_set_inode_bit(char *bitmap, unsigned int ino)
{
	int shift = (ino)/8;		/* номер байта */
	int offset = (ino)%8;		/* номер бита в байте */
	*(bitmap+shift) = ((1<<offset)|(*(bitmap+shift)));
	printk(KERN_ALERT "myfs: new mask for inode(%u) = 0x%x.\n", ino, *(bitmap+shift));
	return;
}

/*!
	функция устанавливает соответствующий и-узлу бит в карте битов в 0
*/
static void myfs_clear_inode_bit(char *bitmap, unsigned int ino)
{
	int shift = (ino)/8;		/* номер байта */
	int offset = (ino)%8;		/* номер бита в байте */
	printk(KERN_ALERT "myfs: mask for inode(%u) = 0x%x.\n", ino, *(bitmap+shift));
	*(bitmap+shift) = ((~(1<<offset))&(*(bitmap+shift)));
	return;
}

/*!
	функция инкрементирует счётчит свободных и-узлов, обновляет суперблок
*/
static void myfs_release_inode(struct super_block *sb)
{
	struct myfs_sb_info *msi = MYFS_SB(sb);
	msi->s_free_inodes_count++;
	BUG_ON(msi->s_free_inodes_count > msi->s_inodes_count);
	sb->s_dirt = 1;
	return;
}

/*!
	функция удаляет и-узел, освобождает занимаемую им память и пространство,
	устанавливает соответствующий и-узлу бит в карте битов в 0
*/
void myfs_free_inode(struct inode *inode)
{
	struct buffer_head *bitmap_bh = NULL;
	struct super_block *sb = inode->i_sb;
	struct myfs_super_block *myfs_sb = MYFS_SB(sb)->s_mfs;
	unsigned int ino = inode->i_ino;

	BUG_ON(!myfs_sb);
	printk(KERN_ALERT "myfs: freeing inode %u.\n", ino);
	if (ino <= myfs_sb->s_first_nonmeta_inode || ino >= myfs_sb->s_inodes_count) 
	{
		printk(KERN_ALERT "myfs: invalid inode number to be freed %u.\n", ino);
		goto error_return;
	}
	clear_inode(inode);
	bitmap_bh = read_inode_bitmap(sb);
	if (inode_already_freed(bitmap_bh->b_data, ino))
	{
		printk(KERN_ALERT "myfs: inode already free %u.\n", ino);
		goto error_return;
	}
	myfs_clear_inode_bit(bitmap_bh->b_data, ino);
	myfs_release_inode(sb);
	mark_buffer_dirty(bitmap_bh);
error_return:

	return;
}

/*!
	функция поиска свободного бита для нового и-узла в карте битов
 */
static unsigned int myfs_find_free_inode(unsigned char *bitmap, struct super_block *sb)
{
	struct myfs_sb_info *msbi = MYFS_SB(sb);
	unsigned char *end = bitmap + sb->s_blocksize;
	unsigned int ino = 0;
	unsigned char *start = bitmap + (msbi->s_first_nonmeta_inode)/8;

	while (start < end)
	{
		int i;
		if (*start == 0xff)
		{
		       start++;
		       continue;	       
		}
		for(i=0;i<8;i++)
			if (!((*start)&(1<<i)))
			{
				ino = ((int)(start-bitmap))*8 + i;
				if(ino >= 32768 || ino >= msbi->s_inodes_count)
				{
					ino = 0;
					return ino;
				}
				printk(KERN_ALERT "myfs_find_free_inode number %d", ino);
				return ino;
			}
	
	}
	return ino;
}

/*!
	функция возвращает готовый инициализированный и-узел
*/
struct inode *myfs_new_inode(struct inode *dir, int mode)
{
	struct super_block *sb = dir->i_sb;
	struct myfs_sb_info *msbi = MYFS_SB(sb);
	struct myfs_inode_info *msi;
	struct buffer_head *bitmap_bh = NULL;
	struct inode *inode;
	unsigned int ino = 0;

	inode = new_inode(sb);
	if(!inode)
	{
		printk(KERN_ALERT "myfs: could not allocate inode from inode cache.\n");
		return ERR_PTR(-ENOMEM);
	}
	msi = MYFS_I(inode);

	bitmap_bh = read_inode_bitmap(sb);
	ino = myfs_find_free_inode(bitmap_bh->b_data, sb);
	if(!ino)
	{
		printk(KERN_ALERT "myfs: could not find any free inode. File system full.\n");
		return ERR_PTR(-ENOSPC);
	}
	printk("myfs: allocated new inode (%u).\n", ino);
	myfs_set_inode_bit(bitmap_bh->b_data, ino);
	inode->i_ino = ino;
	inode->i_mode = mode;
	inode->i_gid = current->cred->gid;
	inode->i_uid = current->cred->uid;
	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;

	printk(KERN_ALERT "myfs: successfully allocated inodes....\n");
	memset(msi->i_data, 0, sizeof(msi->i_data));
	msi->i_data[0] = ino;
	msi->state = MYFS_INODE_ALLOCATED;
	msbi->s_free_inodes_count --;
	sb->s_dirt = 1;
	insert_inode_hash(inode);
	mark_inode_dirty(inode);
	mark_buffer_dirty(bitmap_bh);
	sync_dirty_buffer(bitmap_bh);
	printk(KERN_ALERT "myfs: returning now.\n");
	return inode;
}
